//+---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation, 2008
//
// File: XmlEncoder.cs
//
// Contents:
//       Class to encode a string according to our xml encoding rules
//       (this basically means encoding invalid xml characters to entities
//
// Created by:
//       Adam Wardynski (AdamWa) 2008, Apr 14
//
//----------------------------------------------------------------------------
using System;
using System.Text;
using System.Web;

namespace Microsoft.Reputation.Services
{
    /// <summary>
    ///     Class to encode a string according to MRS xml encoding rules.
    ///     Refer to Encode method for details.
    /// </summary>
    public static class XmlEncoder
    {
        /// <summary>
        ///     Encodes a string according to MRS xml encoding rules.
        ///     This means encoding the characters defined by the XML spec to be invalid.
        ///     They are being encoded as character entities coded with a hex number with no 
        ///     trailing zeroes. E.g. 0x01 character becomes &#x1; entity. Additionally, each
        ///     ampersand in the original input is encoded using the named entity &amp;
        ///     Note this does not encode valid surrogate pairs, though it does encode single
        ///     surrogate characters without proper pair.
        ///     It is valid to pass a null string or an empty string - in such scenario the
        ///     encoded version is the same as the original, null or empty string.
        /// </summary>
        /// <param name="s">
        ///     Input string to encode.
        /// </param>
        /// <returns>
        ///     Encoded version of the string.
        /// </returns>
        public static string Encode(string s)
        {
            if (string.IsNullOrEmpty(s))
            {
                return s;
            }
            StringBuilder sb = new StringBuilder(s.Length);
            for (int i = 0; i < s.Length; i++)
            {
                switch (s[i])
                {
                    //
                    // Ampersand special case
                    //
                    case '&':
                        sb.Append("&amp;");
                        continue;
                    //
                    // Valid character case
                    //
                    case '\t':
                        sb.Append(s[i]);
                        continue;
                    //
                    // To-be-encoded character case (xml invalid + \r + \n)
                    //
                    case '\r':
                    case '\n':
                    case '\xFFFE':
                    case '\xFFFF':
                        sb.AppendFormat("&#x{0:X};", (int)s[i]);
                        continue;
                    //
                    // Advanced checking for valid (aka '\t') or to-be-encoded (aka '\xFFFF') case
                    //
                    default:
                        if(s[i] < '\x20')
                        {
                            goto case '\xFFFF';
                        }
                        if (char.IsSurrogate(s[i]))
                        {
                            if (!char.IsSurrogatePair(s, i))
                            {
                                goto case '\xFFFF';
                            }
                            //
                            // Valid surrogate pair. So we copy first surrogate and increment i..
                            //
                            sb.Append(s[i++]);
                            //
                            // ..and then we go to the valid case which is going to copy second surrogate
                            //
                        }
                        goto case '\t';
                }
            }
            return sb.ToString();
        }

        /// <summary>
        ///     Encodes a string according to MRS xml encoding rules.
        ///     This basically means running Html decoder, as characters are
        ///     encoded using character entities
        /// </summary>
        /// <param name="s">
        ///     string to decode
        /// </param>
        /// <returns>
        ///     Decoded string
        /// </returns>
        public static string Decode(string s)
        {
            if (string.IsNullOrEmpty(s))
            {
                return s;
            }
            return HttpUtility.HtmlDecode(s);
        }

        public static string Base64Encode(string s)
        {
            string base64String = null;

            try
            {
                if (!string.IsNullOrEmpty(s))
                {
                    byte[] buff = System.Text.Encoding.UTF8.GetBytes(s);
                    base64String = Convert.ToBase64String(buff);
                }
            }
            catch (Exception ex)
            {
                TraceUtility.TraceCatchException(Tracing.Source, ex);
                base64String = null;
            }

            return base64String;
        }

        public static string Base64Decode(string s)
        {
            string utf8String = null;

            try
            {
                if (!string.IsNullOrEmpty(s))
                {
                    byte[] buff = Convert.FromBase64String(s);
                    utf8String = System.Text.Encoding.UTF8.GetString(buff);
                }
            }
            catch (Exception ex)
            {
                TraceUtility.TraceCatchException(Tracing.Source, ex);
                utf8String = null;
            }

            return utf8String;
        }
    }
}
